package com.d4a5.mapping.core
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.Timer;
	import flash.utils.flash_proxy;
	
	
	public dynamic class StateArray extends Proxy implements IEventDispatcher
	{
		
		protected var _dispatcher:EventDispatcher;
		protected var _hash:Dictionary;
		
		public var allowMultiples:Boolean = false;
		protected var _stateName:String;
		
		protected var _count:int = 0;
		protected var _hashChanged:Boolean = false;
		protected var _delayedDispatchTimer:Timer;
		
		public function StateArray( booleanVariableName:String )
		{
			super();
			this._stateName = booleanVariableName;
			this._dispatcher = new EventDispatcher();
			this._hash = new Dictionary();
			
			this._delayedDispatchTimer = new Timer(1);
			this._delayedDispatchTimer.addEventListener( TimerEvent.TIMER , onDispatchNow );
		}
		
		public function trueHandler( event:Event ) : void {
			this.add( event.target );
		}
		
		public function falseHandler( event:Event ) : void {
			this.remove( event.target );
		}
		
		public function add( item:Object ) : void {
			if( item[ this._stateName ] == false ){
				item[ this._stateName ] = true;
			}
			else if( this._hash[ item ] == null ){
				if( this.allowMultiples == false ){
					this.removeAll();
				}
				this._hash[ this._count ] = item;
				this._hash[ item ] = this._count;
				this._count++;
				this.delayedDispatch();
			}
		}
		
		
		public function remove( item:Object ) : void {
			if( item[ this._stateName ] == true ){
				item[ this._stateName ] = false;
			}
			else if( this._hash[ item ] != null ){
				var curIndex:int = this._hash[ item ];
				if( curIndex + 1 < this._count ){
					var lastItem:Object = this._hash[ this._count-1 ];
					this._hash[ curIndex ] = lastItem;
					this._hash[ lastItem ] = curIndex;
					curIndex = this._count - 1;
				}
				delete this._hash[curIndex];
				delete this._hash[item];
				this._count--;
				this.delayedDispatch();
			}
		}
		
		public function removeAll() : void {
			for( var i:int = this._count-1; i >= 0; i-- ){
				this._hash[ i ][this._stateName] = false;
				delete this._hash[ this._hash[i] ];
				delete this._hash[ i ];
			}
			this._count = 0;
			this.delayedDispatch();
		}
		
		public function get length() : int {
			return this._count;
		}
		
		public function contains( item:Object ) : Boolean {
			return ( this._hash[item] is int ) as Boolean;
		}
		
		override flash_proxy function getProperty(name:*):* {
			return this._hash[name];
		}
		
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			this._dispatcher.addEventListener( type , listener , useCapture , priority , useWeakReference );
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			this._dispatcher.removeEventListener( type , listener , useCapture );
		}
		
		public function dispatchEvent(event:Event):Boolean
		{
			return this._dispatcher.dispatchEvent( event );
		}
		
		public function hasEventListener(type:String):Boolean
		{
			return this._dispatcher.hasEventListener( type );
		}
		
		public function willTrigger(type:String):Boolean
		{
			return this._dispatcher.willTrigger( type );
		}
		
		protected function delayedDispatch() : void {
			if( this._hashChanged == false ){
				this._hashChanged = true;
				this._delayedDispatchTimer.start();
			}
			else{
				this._delayedDispatchTimer.stop();
				this._delayedDispatchTimer.start();
			}
		}
		
		protected function onDispatchNow( event:TimerEvent ) : void {
			this._delayedDispatchTimer.stop();
			this._hashChanged = false;
			this.dispatchEvent( new Event( Event.CHANGE ) );
		}
		
	}
}